//
//  vdRenderer.m
//  Void Dead
//
//  Created by Sidney Just on 20.11.09.
//
//  Copyright © 2009 by Sidney Just
//  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
//  documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
//  and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
//  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#import "vdRenderer.h"
#import "vdKernel.h"
#import "matrix.h"
#import "vdMisc.h"


@implementation vdRenderer
@synthesize objectsIn, objectsOut;

#pragma mark -
#pragma mark Drawing

- (void)drawOpenGLES1Based {
	glColor4f(color->red, color->green, color->blue, color->alpha);
	
	if(texture)
	{
		glBindTexture(GL_TEXTURE_2D, texture.texture);
		vdEnable(GL_TEXTURE_2D);
	} else {
		vdDisable(GL_TEXTURE_2D);
	}
	
	if(useMatrix)
	{
		glLoadMatrixf(matrix);
	} else {
		glLoadIdentity();
	}

	glVertexPointer(positionsPerVertex, GL_FLOAT, 0, vertices);
	vdEnableClientState(GL_VERTEX_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
	
	glDrawArrays(mode, 0, vertexPos/positionsPerVertex);
}

#ifdef ALLOW_ES2
- (void)drawOpenGLES2Based {
	if(!shader)
	{
		shader = defaultShader;
	}
	
	GLfloat proj[16];
	
	if(useMatrix)
	{
		mat4f_LoadProjectionWithoutRotationX(proj, matrix);
		shader.matrix = proj;
	} else {
		mat4f_LoadProjection(proj);
		shader.matrix = proj;
	}

	shader.vertices = vertices;
	shader.texture = texture;
	shader.texCoords = texCoords;
	shader.color = color;
	shader.positionsPerVertex = positionsPerVertex;
	shader.isPostProcessingShader = NO;
		
	glUseProgram(shader.shader);
	[shader runShader];
		
	glDrawArrays(mode, 0, vertexPos/positionsPerVertex);
}
#endif

- (void)extendSize:(int)factor {
	availableVertices += verticesPerObject*factor;
	
	vertices = (GLfloat *)realloc(vertices, availableVertices*sizeof(GLfloat));
	texCoords = (GLfloat *)realloc(texCoords, availableVertices*sizeof(GLfloat));
}

- (void)equalSize {
	int temp = (verticesPerObject*objectsIn)*100;
	if(availableVertices > temp)
	{
		availableVertices = temp;
		vertices = (GLfloat *)realloc(vertices, availableVertices*sizeof(GLfloat));
		texCoords = (GLfloat *)realloc(texCoords, availableVertices*sizeof(GLfloat));
	}
}

- (void)minimize {
	availableVertices = verticesPerObject*100;
	
	vertices = (GLfloat *)realloc(vertices, availableVertices*sizeof(GLfloat));
	texCoords = (GLfloat *)realloc(texCoords, availableVertices*sizeof(GLfloat));
}

- (void)flushContent {
	if(objectsIn > 0)
	{
		objectsOut++;
#ifdef ALLOW_ES2
		if(API == OpenGLESAPI2)
		{
			[self drawOpenGLES2Based];
		} else {
			[self drawOpenGLES1Based];
		}
#else
		[self drawOpenGLES1Based];
#endif
	}
	
	vertexPos = 0;
}


- (void)finalizeScene {
	[self flushContent];
	[self equalSize];
}

- (void)setShader:(vdShader *)_shd {
	if(_shd != shader && API == OpenGLESAPI2)
		[self flushContent];
	
	shader = _shd;
}

- (void)setMode:(GLenum)_mode {
	if(_mode != mode)
		[self flushContent];
	
	mode = _mode;
}

- (void)setBlendMode:(BOOL)enabled sFactor:(GLenum)sfactor dFactor:(GLenum)dfactor {
	if(enabled != blendEnabled || sfactor != sBlendFactor || dfactor != dBlendFactor)
		[self flushContent];
	
	if(enabled) {
		vdEnable(GL_BLEND);
		
		sBlendFactor = sfactor;
		dBlendFactor = dfactor;
		glBlendFunc(sBlendFactor, dBlendFactor);
	} else {
		vdDisable(GL_BLEND);
	}
	
	blendEnabled = enabled;
}


- (void)addVertices:(GLfloat *)_ver texCoords:(GLfloat *)_tex posPerVertex:(int)_ppo size:(int)_size texture:(vdTexNode *)_texNode color:(vdColor *)_col {
	objectsIn += 1;
	useMatrix = NO;
	
	int oldSize = verticesPerObject;
	verticesPerObject = _size;
	
	if(availableVertices == 0)
	{
		[self extendSize:200];
		
		vertexPos = 0;
	} 

	if(texture != _texNode || vdColor_equalToColor(_col, color) == NO || _size != oldSize || _ppo != positionsPerVertex)
	{
		[self flushContent];
	} 
	
	if(availableVertices <= vertexPos + verticesPerObject)
	{
		[self extendSize:100]; 
	} 
	
	for(int i=0; i<verticesPerObject; i++)
	{
		vertices[vertexPos+i] = _ver[i];
		texCoords[vertexPos+i] = _tex[i];
	}
	
	vertexPos += verticesPerObject;
	texture = _texNode;
	
	positionsPerVertex = _ppo;
	
	color->alpha = _col->alpha;
	color->red = _col->red;
	color->green = _col->green;
	color->blue = _col->blue;
}

- (void)addVertices:(GLfloat *)_ver texCoords:(GLfloat *)_tex posPerVertex:(int)_ppo size:(int)_size texture:(vdTexNode *)_texNode color:(vdColor *)_col andMatrix:(GLfloat *)_matrix {
	[self flushContent];
	[self addVertices:_ver texCoords:_tex posPerVertex:_ppo size:_size texture:_texNode color:_col];
	
	useMatrix = YES;
	for(int i=0; i<16; i++)
	{
		matrix[i] = _matrix[i];
	}
	
	[self flushContent];
}

- (void)setFocus {
	useMatrix = NO;
	
	texture = NULL;
	shader = NULL; // Set the shader back
	mode = GL_TRIANGLES;
	objectsOut = 0;
	objectsIn = 0;
	
	positionsPerVertex = 2;
	
	backingWidth = [vdKernel sharedKernel].renderView.backingWidth;
	backingHeight = [vdKernel sharedKernel].renderView.backingHeight;
	
	API = [vdKernel sharedKernel].rAPI;
}

#pragma mark -
#pragma mark Con- Destructor

static vdRenderer *_sharedRenderer = NULL;

+ (vdRenderer *)sharedRenderer {
	if(!_sharedRenderer)
	{
		_sharedRenderer = [[vdRenderer alloc] init];
	}
	
	return _sharedRenderer;
}

- (id)init {
	if(!_sharedRenderer)
	{
		if(self = [super init])
		{
			availableVertices = 0;
			vertexPos = 0;
			verticesPerObject = 0;
			objectsIn = 0;
			objectsOut = 0;
			mode = GL_TRIANGLES;
			
			positionsPerVertex = 2;
			
			vertices = (GLfloat *)malloc(sizeof(GLfloat));
			texCoords = (GLfloat *)malloc(sizeof(GLfloat));
			
			blendEnabled = YES;
			sBlendFactor = GL_SRC_ALPHA;
			dBlendFactor = GL_ONE_MINUS_SRC_ALPHA;
			
			color = (vdColor *)malloc(sizeof(vdColor));
			
			vdEnable(GL_BLEND);
			glBlendFunc(sBlendFactor, dBlendFactor);
			
			
			defaultShader = [[vdShaderLib sharedLib] getShader:vdShaderDefault];
			
		}
		_sharedRenderer = self;
		return self;
	}
	return _sharedRenderer;
}

- (void)dealloc {
	if(self == _sharedRenderer)
		_sharedRenderer = NULL;
	
	free(vertices);
	free(texCoords);
	
	free(color);
	
	[super dealloc];
}

@end
